/*
    FormatItem类主要负责⽇志消息⼦项的获取及格式化
    设计思想：
        通过多态设计一个抽象基类，抽象出一个格式化接口
        然后派生出不同格式化子类，每个子类实现一种功能
*/

#ifndef __M_FORMAT_H__
#define __M_FORMAT_H__

#include"message.hpp"
#include<memory>
#include<vector>
#include<sstream>
#include<cassert>

namespace Hxy_Log
{
    //FormatItem基类，抽象出格式化接口
    class FormatItem
    {
        public:
            using ptr = std::shared_ptr<FormatItem>;
            virtual void format(std::ostream& out,LogMsg& msg) = 0;
    };
    //派生出不同子项子类，实现 : 信息，等级，时间，文件名，行号，线程id,日志器名，制表符，换行，其他
    class MsgFormatItem : public FormatItem
    {
        public:
            MsgFormatItem(const std::string&){}
            virtual void format(std::ostream& out,LogMsg& msg)
            {
                out<<msg._payload;
            }
    };
    class LevelFormatItem : public FormatItem
    {
        public:
            LevelFormatItem(const std::string&){}
            virtual void format(std::ostream& out,LogMsg& msg)
            {
                out<<LogLevel::ToString(msg._value);
            }
    };
    class TimeFormatItem : public FormatItem
    {
        public:
            TimeFormatItem(const std::string& str = "%H:%M:%S"):_time_fmt(str){}
            virtual void format(std::ostream& out,LogMsg& msg)
            {
                struct tm t;
                localtime_r(&msg._ctime,&t);    //对时间戳进行转换成时分秒，转换后的值放到struct tm中
                char ch[128] = {0};
                strftime(ch,sizeof ch-1,_time_fmt.c_str(),&t); //把struct tm中的值通过_time_fmt格式化方式放到字符数组ch中
                out<<ch;
            }
        private:
            std::string _time_fmt;
    };
    class FileFormatItem : public FormatItem
    {
        public:
            FileFormatItem(const std::string&){}
            virtual void format(std::ostream& out,LogMsg& msg)
            {
                out<<msg._file;
            }
    };
    class LineFormatItem : public FormatItem
    {
        public:
            LineFormatItem(const std::string&){}
            virtual void format(std::ostream& out,LogMsg& msg)
            {
                out<<msg._line; //osteam自动会将数字转化成字符数字
            }
    };
    class ThreadFormatItem : public FormatItem
    {
        public:
            ThreadFormatItem(const std::string&){}
            virtual void format(std::ostream& out,LogMsg& msg)
            {
                out<<msg._tid;
            }
    };
    class NameFormatItem : public FormatItem
    {
        public:
            NameFormatItem(const std::string&){}
            virtual void format(std::ostream& out,LogMsg& msg)
            {
                out<<msg._name;
            }
    };
    class TabFormatItem : public FormatItem
    {
        public:
            TabFormatItem(const std::string&){}
            virtual void format(std::ostream& out,LogMsg& msg)
            {
                out<<"\t";
            }
    };
    class NewLineFormatItem : public FormatItem
    {
        public:
            NewLineFormatItem(const std::string&){}
            virtual void format(std::ostream& out,LogMsg& msg)
            {
                out<<"\n";
            }
    };
    class OtherFormatItem : public FormatItem
    {
        public:
            OtherFormatItem(const std::string& other = ""):_other(other){}
            virtual void format(std::ostream& out,LogMsg& msg)
            {
                out<<_other;
            }
        private:
            std::string _other;
    };

    /*
        pattern成员：保存⽇志输出的格式字符串。
        %d ⽇期
        %T 缩进
        %t 线程id
        %p ⽇志级别
        %c ⽇志器名称
        %f ⽂件名
        %l ⾏号
        %m ⽇志消息
        %n 换⾏
        [%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n
    */
   class Formatter
   {
    public:
        using ptr = std::shared_ptr<Formatter>;
        Formatter(const std::string pattern = "[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n")
        :_pattern(pattern)
        {
            assert(parsePattern()); //对_pattern进行格式化规则字符串分析
        }
        //对msg进行格式化
        std::string format(LogMsg& msg)
        {
            std::stringstream ss;
            for(auto item:_items)
            {
                item->format(ss,msg);
            }
            return ss.str();
        }
        void format(std::ostream& out,LogMsg& msg)
        {
            for(auto item : _items)
            {
                item->format(out,msg);
            }
        }
    private:
        //判断是不是格式化字符
        bool _Isalpha(const char& ch){return ch == 'd'||ch == 'T'||ch == 't'||ch == 'p'||ch == 'c'||ch == 'f'||ch =='l'||ch =='m' ||ch =='n';}
        //格式化规则字符串解析
        bool parsePattern()
        {
            //abcde[%%%d]
            //abcde[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n
            //进行处理格式化规则字符串
            std::vector<std::pair<std::string ,std::string>> _create_items;
            int right = 0;
            std::string key,val;
            while(right<_pattern.size())
            {
                if(_pattern[right] != '%')
                {
                    val+=_pattern[right++]; //处理普通字符串
                    continue;
                }
                //走到这肯定是%，判断right+1 是否为%
                if(right+1 < _pattern.size()&&_pattern[right+1] == '%')
                {
                    val+='%';
                    right+=2;
                    continue;
                }
                //走到这right的位置是%但是right+1不是%
                //如果val里面存放了普通字符，先进行处理val里面的普通字符
                if(val.size()!=0)
                {
                    _create_items.push_back(std::make_pair(key,val));
                    key.clear();val.clear();
                }
                right++;    //走到特殊字符的位置，判断特殊字符后面是否接了子规则子串{}
                if( right<_pattern.size()&&_Isalpha(_pattern[right])) key+=_pattern[right++];//把特殊字符子串放入key，走到下一位
                else{
                    std::cout<<"%匹配出错\n";
                    return false;
                }    
                bool flag = false;
                if(right < _pattern.size()&&_pattern[right] == '{')  //如果下一位是子规则子串{，把{}里面的全加到val中
                {
                    while(right < _pattern.size()&&_pattern[right+1]!='}')
                    {
                        //进来是在'{'的位置，用前置++可能会把'}'也放进去，所以判断的时候需要判断right+1的位置
                        val+=_pattern[++right];
                        flag = true;    
                    }
                    if(right >= _pattern.size())    //证明传入的格式化规则字符串不是合法的
                    {
                        std::cout<<"子规则{}匹配出错！\n";
                        return false;
                    }
                }
                if(flag)    //如果flag等于true 需要先处理规则子串
                {
                    _create_items.push_back(std::make_pair(key,val));
                    right+=2;   //right 停留在}前面，需要走两步到下一个判断点
                    key.clear();val.clear();
                    continue;
                }
                //如果规则字符后面不是{},则直接处理规则字符
                _create_items.push_back(std::make_pair(key,val));
                key.clear();val.clear();
            }
            
            //将处理好的格式化规则字符串数组放进_items里面
            for(auto& [first,second] : _create_items)
            {   
                _items.push_back(createItems(first,second));
            }
            return true;
        }

        //根据不同的格式化字符串，创建不同的格式化对象
        FormatItem::ptr createItems(const std::string& key,const std::string& val)
        {
           if(key == "d"){return std::make_shared<TimeFormatItem>(val);}
           else if(key == "T"){return std::make_shared<TabFormatItem>(val);}
           else if(key == "t"){return std::make_shared<ThreadFormatItem>(val);}
           else if(key == "p"){return std::make_shared<LevelFormatItem>(val);}
           else if(key == "f"){return std::make_shared<FileFormatItem>(val);}
           else if(key == "l"){return std::make_shared<LineFormatItem>(val);}
           else if(key == "m"){return std::make_shared<MsgFormatItem>(val);}
           else if(key == "c"){return std::make_shared<NameFormatItem>(val);}
           else if(key == "n"){return std::make_shared<NewLineFormatItem>(val);}
           return std::make_shared<OtherFormatItem>(val);
        }
    private:
        std::string _pattern;   //格式化规则字符串
        std::vector<FormatItem::ptr> _items;
   };
}

#endif